#include "hextobin.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/********************************************************************************
input:
    c:单个字符('0'~'9' 'a'~'f', 'A'~'F')
output:
    单个字符转化为单个字符
********************************************************************************/
static uint8_t HexCharToBinBinChar(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    else if (c >= 'a' && c <= 'z')
        return c - 'a' + 10;
    else if (c >= 'A' && c <= 'Z')
        return c - 'A' + 10;
    return 0xff;
}

/********************************************************************************
input:
    p: 两个文本字符
output:
    转化为1个字节
********************************************************************************/
static uint8_t Hex2Bin(const char *p)
{
    uint8_t tmp = 0;
    tmp = HexCharToBinBinChar(p[0]);
    tmp <<= 4;
    tmp |= HexCharToBinBinChar(p[1]);
    return tmp;
}

/********************************************************************************
input:
    src: hex单行字符串
    p->type: 如果函数返回结果正确，这里就存着转化后的类型
    p->len: 如果函数运行正确，这里就存着转化后的bin数据长度
    p->data: 如果函数运行正确，长度并且不为0，该指针就只想转化后的数据
    p->addr[0]: 如果函数返回结果正确，这里就存着地址的低字节
    p->addr[1]: 如果函数返回结果正确，这里就存着地址的低字节
output:
    返回hex格式流分析的结果
********************************************************************************/
static RESULT_STATUS HexFormatUncode(const char *src, BinFarmat *p)
{
    uint8_t check = 0, tmp[4], binLen;
    uint16_t hexLen = strlen(src);
    uint16_t num = 0, offset = 0;
    if (hexLen > HEX_MAX_LENGTH)		//数据内容过长
        return RES_DATA_TOO_LONG;
    if (hexLen < HEX_MIN_LEN)
        return RES_DATA_TOO_SHORT;	//数据内容过短
    if (src[0] != ':')
        return RES_NO_COLON;		//没有冒号
    if ((hexLen - 1) % 2 != 0)
        return RES_LENGTH_ERROR;	//hexLen的长度应该为奇数
    binLen = (hexLen - 1) / 2;		//bin总数据的长度，包括长度，地址，类型校验等内容
    while (num < 4)
    {
        offset = (num << 1) + 1;
        tmp[num] = Hex2Bin(src + offset);
        check += tmp[num];
        num++;
    }
    p->len = tmp[0];			//把解析的这些数据保存到结构体中
    p->addr = tmp[1];
    p->addr <<= 8;
    p->addr += tmp[2];
    p->type = tmp[3];
    while (num < binLen)
    {
        offset = (num << 1) + 1;        //保存真正的bin格式数据流
        p->data[num - 4] = Hex2Bin(src + offset);
        check += p->data[num - 4];
        num++;
    }
    if (p->len != binLen - 5)		//检查hex格式流的长度和数据的长度是否一致
        return RES_LENGTH_ERROR;
    if (check != 0)				//检查校验是否通过
        return RES_CHECK_ERROR;
    return RES_OK;
}

RESULT_STATUS HexFile2BinFile(char *src, char *dest)
{
    FILE *src_file, *dest_file;
    uint32_t addr_base = 0;
    int64_t first_addr = -1;
    uint16_t addr_low = 0;
    uint32_t addr_hign = 0;
    uint32_t addr_mid = 0;
    uint32_t entry = 0;
    char buffer_hex[600];
    uint8_t buffer_bin[255];
    BinFarmat gBinFor;
    RESULT_STATUS res;
    gBinFor.data = buffer_bin;
    src_file = fopen(src, "r");		//以文本的形式打开一个hex文件
    if (!src_file)
        return RES_HEX_FILE_NOT_EXIST;
    dest_file = fopen(dest, "wb");		//以二进制写的方式打开文件，文件不存在也没影响
    if (!dest_file)
        return RES_BIN_FILE_PATH_ERROR;
    fseek(src_file, 0, SEEK_SET);           //定位到开头，准备开始读取数据
    while (!feof(src_file))
    {
        fscanf(src_file, "%s\r\n", buffer_hex);
        res = HexFormatUncode(buffer_hex, &gBinFor);
        if (res != RES_OK)
        {
            fclose(src_file);
            fclose(dest_file);
            return res;
        }
        switch (gBinFor.type)
        {
        case 0:			//数据记录
        {
            addr_low = gBinFor.addr;
            if(first_addr == -1){
                first_addr = gBinFor.addr;
            }
                //数据指针偏移
            uint32_t offset = addr_low + addr_mid + addr_hign - addr_base - first_addr;
            fseek(dest_file, offset, SEEK_SET);
            if (fwrite((const uint8_t*)gBinFor.data, gBinFor.len, 1, dest_file) != 1)
            {
                fclose(src_file);
                fclose(dest_file);
                return RES_WRITE_ERROR;
            }
            break;
        }
        case 1:		        //数据结束
            fclose(src_file);
            fclose(dest_file);
            return RES_OK;
        case 2:
        {
            uint32_t addr_temp = (gBinFor.data[0]<<8)|(gBinFor.data[1]);
            addr_mid = addr_temp << 4;
            break;
        }
        case 4:			//线性段地址
        {
            uint32_t addr_temp = (gBinFor.data[0]<<8)|(gBinFor.data[1]);
            addr_hign = addr_temp << 16;
            if(addr_base == 0){
                addr_base = addr_hign;
            }
            break;
        }
        case 5:         //起始线性地址记录标识
            entry = (gBinFor.data[0]<<24)|(gBinFor.data[1]<<16)|(gBinFor.data[2]<<8)|(gBinFor.data[3]);
            break;
        default:
            fclose(src_file);
            fclose(dest_file);
            return RES_TYPE_ERROR;
        }
    }
    fclose(src_file);
    fclose(dest_file);
    return RES_HEX_FILE_NO_END;
}